<?php

/* ========================================================================= */
/* This file is part of the HypnoPHP Framework Library                       */
/* Copyright (c) 2009-2010, Radu Buzea                                       */
/* ========================================================================= */

/**
* @author Radu Buzea
* @copyright Copyright (c) 2009-2010, Radu Buzea (www.hypnosis.ro)
* @license GNU Lesser General Public License (www.gnu.org/copyleft/lesser.html)
* @package HypnoPHP
* @category Core
* @version 1.0.3
*/

defined('HYPNOPHP_APPLICATION') or
die("Cannot use <i>" . basename(__FILE__) . "</i> outside HypnoPHP Application");

// ============================================================================
// Final class: HFactory
// ============================================================================

final class HErrorHandler
{

	/**
	* Internal handling errors during runtime
	*
	* @param integer $error_no The error code
	* @param string $error_text The error text
	* @param string $error_file The script file that calls the error
	* @param integer $error_line The line in the script file where the error was called
	* @return void
	* @access public
	* @static
	*/
	public static function app_global_error_handler($error_no = 0, $error_text = '',
		$error_file = '', $error_line = 0)
	{
		$err_reporting = error_reporting();
		if ($err_reporting == 0) { return; }
		$debug_mode = HApplication::getConfig('debug_mode');
		switch ($error_no) {
			case E_ERROR:
				if (($err_reporting | E_ERROR) != $err_reporting) { return true; }
			case E_USER_ERROR:
				if (($err_reporting | E_USER_ERROR) != $err_reporting) { return true; }
			case E_COMPILE_ERROR:
				if (($err_reporting | E_COMPILE_ERROR) != $err_reporting) { return true; }
			case E_CORE_ERROR:
				if (($err_reporting | E_CORE_ERROR) != $err_reporting) { return true; }
				$message_title = "FATAL ERROR";
				$error_text .=  "\nError called in file '$error_file' on line: '$error_line'";
				$abort = true;
				break;
			case E_WARNING:
				if (($err_reporting | E_WARNING) != $err_reporting) { return true; }
			case E_USER_WARNING:
				if (($err_reporting | E_USER_WARNING) != $err_reporting) { return true; }
			case E_COMPILE_WARNING:
				if (($err_reporting | E_COMPILE_WARNING) != $err_reporting) { return true; }
				$message_title = "WARNING";
				$error_text .=  "\nWarning called in file '$error_file' on line: '$error_line'";
				break;
			case E_PARSE:
				if (($err_reporting | E_PARSE) != $err_reporting) { return true; }
				$message_title = "PARSER ERROR";
				$error_text .=  "\nError called in file '$error_file' on line: '$error_line'";
				$abort = true;
				break;
			case E_STRICT:
				if (!$debug_mode || ($err_reporting | E_STRICT) != $err_reporting) { return true; }
				$message_title = "COMPATIBILITY WARNING";
				$error_text .=  "\nWarning called in file '$error_file' on line: '$error_line'";
				break;
			// E_DEPRECATED - Available since PHP 5.3.0
			case 8192:
				if (!$debug_mode || ($err_reporting | 8192) != $err_reporting) { return true; }
			// E_USER_DEPRECATED - Available since PHP 5.3.0
			case 16384:
				if (!$debug_mode || ($err_reporting | 16384) != $err_reporting) { return true; }
				$message_title = "DEPRECATED WARNING";
				$error_text .=  "\nWarning called in file '$error_file' on line: '$error_line'";
				break;
			// E_RECOVERABLE_ERROR - Available since PHP 5.2.0
			case 4096:
				if (($err_reporting | 4096) != $err_reporting) { return true; }
				$message_title = "RECOVERABLE ERROR";
				$error_text .=  "\nError called in file '$error_file' on line: '$error_line'";
				$abort = true;
				break;
			case E_NOTICE:
				if (!$debug_mode || ($err_reporting | E_NOTICE) != $err_reporting) { return true; }
			case E_USER_NOTICE:
				if (!$debug_mode || ($err_reporting | E_USER_NOTICE) != $err_reporting) { return true; }
				$message_title = "NOTIFICATION";
				$error_text .=  "\nNotification called in file '$error_file' on line: '$error_line'";
				break;
			default:
				if (!$debug_mode) { return true; }
				$message_title = "UNKNOWN ERROR";
				$error_text .=  "\nMessage called in file '$error_file' on line: '$error_line'";
				break;
		}
		echo "\n" . $message_title . "\n--------------------------------------\n" . $error_text . "\n";
		if ($abort) { exit(1); }
		return true;
	}

	# ###########################################################################

	/**
	* Internal handling errors during runtime - with error reporting
	*
	* @param integer $error_no The error code
	* @param string $error_text The error text
	* @param string $error_file The script file that calls the error
	* @param integer $error_line The line in the script file where the error was called
	* @return void
	* @access public
	* @static
	*/
	public static function global_error_handler($error_no = 0, $error_text = '', $error_file = '', $error_line = 0)
	{
		$errors = error_reporting();
		$backtrace = debug_backtrace();

		if (strstr(HRequest::server()->content_type, 'application/')) {
			return self::app_global_error_handler($error_no, $error_text, $error_file, $error_line);
		}

		$debug_mode = false;
		$debug_link = false;
		global $_HAPPLICATION_PATHS, $_HAPPLICATION_ERRLINK_ID;
		$_HAPPLICATION_ERRLINK_ID++;
		$error_uid = 'err' . $_HAPPLICATION_ERRLINK_ID;
		if (count($backtrace) > 1) {
			$trace_text = "<br /><br />" . PHP_EOL . "<div class=\"hypno_details_btn\"><a " .
				"href=\"#{$error_uid}\" onclick=\"return show_error_details('{$error_uid}')\">".
				"<span id=\"{$error_uid}\">Show debug backtrace</span></a></div>" . PHP_EOL .
				"<ol id=\"ls{$error_uid}\" class=\"hypno_details_list\" style=\"display:none\">" . PHP_EOL;
			$btcnt = count($backtrace);
			$debug_mode = HApplication::getConfig('debug_mode');
			if ($debug_mode == null) { $debug_mode = HGLOBAL_DEBUG_MODE; }
			if ($debug_mode) {
				$debug_link = HInternalUI::checkDebugViewer();
				if ($debug_link) {
					$site_root = HApplication::getConfig('site_root');
					if (empty($site_root)) { $site_root = '/'; }
					if (isset($site_root[2]) && substr($site_root, -1, 1) != '/') { $site_root .= '/'; }
					if (HApplication::rewriteMode()) {
						$debug_link = "{$site_root}hypnophp_debug_viewer?fn=";
					} else {
						$debug_link = "{$site_root}?/hypnophp_debug_viewer&fn=";
					}
				}
			}
			$line_with_error = true;
			for ($i = 1; $i < $btcnt; $i++) {
				$function = $backtrace[$i]['function'];
				if (isset($backtrace[$i]['line'])) { $line = $backtrace[$i]['line']; }
				else { $line = 0; }
				if (isset($backtrace[$i]['file'])) { $file = $backtrace[$i]['file']; }
				else { $file = ''; }
				$class = isset($backtrace[$i]['class']) ? $backtrace[$i]['class'] : '';
				$type = isset($backtrace[$i]['type']) ? $backtrace[$i]['type'] : '';
				$args = isset($backtrace[$i]['args']) ? $backtrace[$i]['args'] : array();
				$grayed = ($i < 3 && (strpos($function, 'trigger_error') !== false || strpos($function, 'add_error') !== false));
				$trace_text .= "\t<li" . ($grayed ? " style=\"color:#757575\"" : ($line_with_error ? " style=\"color:#dc2500\"" : "")) . "><code>";
				if ($class != '' && $type != '') { $trace_text .= "{$class}{$type}"; }
				$trace_text .= "{$function}(";
				if (is_array($args) && count($args) > 0) {
					foreach ($args as $akey => $aval) {
						if ($akey > 0) { $trace_text .= ", "; }
						if (is_bool($aval)) { $trace_text .= $aval ? "TRUE" : "FALSE"; }
						else if (is_null($aval)) { $trace_text .= "NULL"; }
						else if (is_string($aval)) {
							if (strlen($aval) > 36) {
								if (strlen($aval) > 1024) {
									$aval = substr($aval, 0, 510) . PHP_EOL . '...' . PHP_EOL . substr($aval, -510, 510);
								}
								$aval_prefix = '<span title="' . htmlentities($aval) . '" style="cursor:default" ' .
									'onmouseover="this.style.backgroundColor=\'#fffde5\'" ' .
									'onmouseout="this.style.backgroundColor=\'transparent\'">\'';
								$aval_suffix = '\'</span>';
								if (strpos($aval, '/') !== false) {
									$p1 = strpos($aval, '/', 1);
									$p2 = strpos($aval, '/', strlen($aval) - 24);
									if ($p1 !== false && $p2 !== false && $p1 > 1 && $p1 < $p2) {
										$aval = $aval_prefix . htmlentities(substr($aval, 0, $p1 + 1) . '...' . substr($aval, $p2)) . $aval_suffix;
									} else {
										$aval = $aval_prefix . htmlentities(substr($aval, 0, 12) . '...' . substr($aval, -24)) . $aval_suffix;
									}
								} else {
									$aval = $aval_prefix . htmlentities(substr($aval, 0, 12) . '...' . substr($aval, -24)) . $aval_suffix;
								}
								$trace_text .= "{$aval}";
							} else {
								$aval = strip_tags($aval);
								$trace_text .= "'{$aval}'";
							}
						} else {
							if (is_object($aval) || is_array($aval)) {
								$inlink_uid = $error_uid . '-' . $i . '-' . $akey;
								$aval_display = is_object($aval) ? get_class($aval) . ' Object' : 'Array';
								$trace_text .= "[<a href=\"#{$inlink_uid}\" onclick=\"return " .
									"special_value_display('{$inlink_uid}')\">{$aval_display}</a>]<pre id=\"{$inlink_uid}\" " .
									"style=\"color:#757575;display:none\">" . print_r($aval, true) . "</pre>";
							} else {
								$trace_text .= "{$aval}";
							}
						}
					}
				}
				if ($debug_link) {
					$file = "<a href=\"{$debug_link}{$file}&ln={$line}#l" . ($line - 1) . "\" style=\"color:#" .
						($grayed ? "757575" : ($line_with_error ? "dc2500" : "0025dc")) . "\">{$file}</a>";
				}
				$trace_text .= ")</code> called in <em>{$file}</em> on line: <em>{$line}</em></li>" . PHP_EOL;
				if (!$grayed && $line_with_error) { $line_with_error = false; }
			}
			$trace_text .= "</ol>" . PHP_EOL;
		}
		preg_match("/(<a href=)[\\'\\\"]function(.*)[\\'\\\"](>)/", $error_text, $m, PREG_OFFSET_CAPTURE);
		if (count($m) > 0 && isset($m[1][0]) && isset($m[2][0]) && isset($m[3][0])) {
			$error_text = substr(strip_tags($error_text), 0, $m[1][1]) . '<a href="http://www.php.net/manual/en/function' .
				$m[2][0] . '.php" target="_blank">' . substr($error_text, $m[3][1] + 1);
		}
		if ($trace_text != '') { $error_text .= $trace_text; }
		if ($debug_link) {
			$error_file = "<a href=\"/{$debug_link}{$error_file}&ln={$error_line}#l" .
				($error_line - 1) . "\" style=\"color:#0025dc\">{$error_file}</a>";
		}
		$abort = false;

		$err_reporting = error_reporting();
		if ($err_reporting == 0) { return; }
		switch ($error_no) {
			case E_ERROR:
				if (($err_reporting | E_ERROR) != $err_reporting) { return true; }
			case E_USER_ERROR:
				if (($err_reporting | E_USER_ERROR) != $err_reporting) { return true; }
			case E_COMPILE_ERROR:
				if (($err_reporting | E_COMPILE_ERROR) != $err_reporting) { return true; }
			case E_CORE_ERROR:
				if (($err_reporting | E_CORE_ERROR) != $err_reporting) { return true; }
				$message_title = "Fatal error";
				$error_text .= "<div>Error called in file <em>$error_file</em> on line: " .
					"<em>$error_line</em><br /><strong style=\"color:#252525\">PHP " . PHP_VERSION .
					" (" . PHP_OS . ") Aborting...</strong></div>" . PHP_EOL;
				$abort = true;
				break;
			case E_WARNING:
				if (($err_reporting | E_WARNING) != $err_reporting) { return true; }
			case E_USER_WARNING:
				if (($err_reporting | E_USER_WARNING) != $err_reporting) { return true; }
			case E_COMPILE_WARNING:
				if (($err_reporting | E_COMPILE_WARNING) != $err_reporting) { return true; }
				$message_title = "Warning";
				$error_text .= "<div>Warning called in file <em>$error_file</em> on line: <em>$error_line</em></div>";
				break;
			case E_PARSE:
				if (($err_reporting | E_PARSE) != $err_reporting) { return true; }
				$message_title = "Parser error";
				$error_text .= "<div>Error called in file <em>$error_file</em> on line: <em>$error_line</em></div>";
				$abort = true;
				break;
			case E_STRICT:
				if (!$debug_mode || ($err_reporting | E_STRICT) != $err_reporting) { return true; }
				$message_title = "Compatibility warning";
				$error_text .= "<div>Warning called in file <em>$error_file</em> on line: <em>$error_line</em></div>";
				break;
			// E_DEPRECATED - Available since PHP 5.3.0
			case 8192:
				if (!$debug_mode || ($err_reporting | 8192) != $err_reporting) { return true; }
			// E_USER_DEPRECATED - Available since PHP 5.3.0
			case 16384:
				if (!$debug_mode || ($err_reporting | 16384) != $err_reporting) { return true; }
				$message_title = "Deprecated warning";
				$error_text .= "<div>Warning called in file <em>$error_file</em> on line: <em>$error_line</em></div>";
				break;
			// E_RECOVERABLE_ERROR - Available since PHP 5.2.0
			case 4096:
				if (($err_reporting | 4096) != $err_reporting) { return true; }
				$message_title = "Recoverable error";
				$error_text .= "<div>Error called in file <em>$error_file</em> on line: " .
					"<em>$error_line</em><br /><strong style=\"color:#252525\">PHP " . PHP_VERSION .
					" (" . PHP_OS . ") Aborting...</strong></div>" . PHP_EOL;
				$abort = true;
				break;
			case E_NOTICE:
				if (!$debug_mode || ($err_reporting | E_NOTICE) != $err_reporting) { return true; }
			case E_USER_NOTICE:
				if (!$debug_mode || ($err_reporting | E_USER_NOTICE) != $err_reporting) { return true; }
				$message_title = "Notification";
				$error_text .= "<div>Notification called in file <em>$error_file</em> on line: <em>$error_line</em></div>";
				break;
			default:
				if (!$debug_mode) { return true; }
				$message_title = "Unknown error";
				$error_text .= "Message called in file <em>$error_file</em> on line: <em>$error_line</em></div>";
				break;
		}
		$done = false;
		if (class_exists('HInternalUI')) {
			$internal_ui_object = HApplication::getAttachedObject('InternalUIObj');
			if (is_null($internal_ui_object)) {
				$internal_ui_object = new HInternalUI();
				HApplication::attachObject('InternalUIObj', $internal_ui_object);
			}
			if (is_object($internal_ui_object) && method_exists($internal_ui_object, 'messageBoxLayout')) {
				echo $internal_ui_object->messageBoxLayout($error_no, $message_title, $error_text, true);
				$done = true;
			}
		}
		if (!$done) { echo "<strong>{$message_title}</strong>: {$error_text}"; }
		if ($abort) { exit(1); }
		return true;
	}

}

# ###########################################################################

// Set the custom error handler
set_error_handler(array('HErrorHandler', 'global_error_handler'));

# ###########################################################################
